import { ComponentType, useContext, useEffect, useRef, useState } from 'react';
import {
  BasePickInputProps,
  MultiPickInput,
  MultiPickResult,
} from '../../pick-input/3d';
import { AutoFocusContext } from './context';
import { useMemoizedFn } from 'ahooks';
import { MouseActionInputProps } from '../../pick-input/mouse-action-input';
import { EventsRes, ICurve } from '@amcax/base';
import { Base2DPickInputProps, Multi2DPickInput } from '../../pick-input/2d';

const PickInputForFocusWrapper = <
  T extends MouseActionInputProps & { onPick?: (...params) => void },
  P extends (onFocusNext: VoidFunction, ...params) => void,
>({
  focusID,
  RenderComponent,
  props,
  onPick,
  canFocusOther = true,
  canBeAutoFocused = true,
  nameRenderer,
  disallowedPickType,
  pickFilter,
  allowPickInvisibleAssembleFromLeftPanel,
}: {
  focusID?: number;
  RenderComponent: ComponentType<T>;
  props: T;
  onPick: P;
  canFocusOther?: boolean;
  canBeAutoFocused?: boolean;
  nameRenderer?: BasePickInputProps['nameRenderer'];
  disallowedPickType?: BasePickInputProps['disallowedPickType'];
  pickFilter?: BasePickInputProps['pickFilter'];
  allowPickInvisibleAssembleFromLeftPanel?: boolean;
}) => {
  const context = useContext(AutoFocusContext);

  const [isActive, setIsActive] = useState(false);

  const registeredID = useRef<number>(null);

  const focus = useMemoizedFn(() => {
    if (!props?.disabled) {
      setIsActive(true);
    }
  });

  const blur = useMemoizedFn(() => {
    setIsActive(false);
  });

  useEffect(() => {
    registeredID.current = context.register(focusID, {
      onFocus: focus,
      onBlur: blur,
      canFocusOther,
      canBeAutoFocused,
    });

    return () => {
      context.unregister(registeredID.current);
    };
  }, []);

  return (
    <RenderComponent
      {...props}
      active={isActive}
      setActive={setIsActive}
      onPick={(...params) => {
        onPick(
          () => {
            if (!canFocusOther) {
              return;
            }
            context.focusNext(registeredID.current);
          },
          ...params,
        );
      }}
      nameRenderer={nameRenderer}
      disallowedPickType={disallowedPickType}
      pickFilter={pickFilter}
      allowPickInvisibleAssembleFromLeftPanel={
        allowPickInvisibleAssembleFromLeftPanel
      }
    />
  );
};

export const PickInputForFocus = <T extends MultiPickResult = MultiPickResult>({
  focusID,
  canFocusOther = true,
  canBeAutoFocused = true,
  nameRenderer,
  disallowedPickType,
  pickFilter,
  allowPickInvisibleAssembleFromLeftPanel,
  ...rest
}: BasePickInputProps<T[]> & {
  focusID?: number;
  canFocusOther?: boolean;
  canBeAutoFocused?: boolean;
  nameRenderer?: BasePickInputProps['nameRenderer'];
  disallowedPickType?: BasePickInputProps['disallowedPickType'];
  pickFilter?: BasePickInputProps['pickFilter'];
  allowPickInvisibleAssembleFromLeftPanel?: boolean;
}) => {
  return (
    <PickInputForFocusWrapper<
      BasePickInputProps<T[]>,
      (
        focusNext: VoidFunction,
        picked?: T[],
        ev?: EventsRes<PointerEvent>,
        needFocusNext?: boolean,
      ) => void
    >
      canFocusOther={canFocusOther}
      canBeAutoFocused={canBeAutoFocused}
      focusID={focusID}
      RenderComponent={MultiPickInput}
      props={{
        ...rest,
      }}
      onPick={(focusNext, val, ev, needFocusNext) => {
        rest.onPick?.(val, ev);

        if (!rest.isMulti && val.length > 0 && needFocusNext) {
          focusNext?.();
        }
      }}
      nameRenderer={nameRenderer}
      disallowedPickType={disallowedPickType}
      pickFilter={pickFilter}
      allowPickInvisibleAssembleFromLeftPanel={
        allowPickInvisibleAssembleFromLeftPanel
      }
    />
  );
};

export const PickInputForFocus2D = ({
  focusID,
  canFocusOther = true,
  canBeAutoFocused = true,
  ...rest
}: Base2DPickInputProps & {
  focusID?: number;
  canFocusOther?: boolean;
  canBeAutoFocused?: boolean;
}) => {
  return (
    <PickInputForFocusWrapper<
      Base2DPickInputProps,
      (focusNext: VoidFunction, ids: string[], picked?: ICurve[]) => void
    >
      canFocusOther={canFocusOther}
      canBeAutoFocused={canBeAutoFocused}
      focusID={focusID}
      RenderComponent={Multi2DPickInput}
      props={{
        ...rest,
      }}
      onPick={(focusNext, val, objs) => {
        rest.onPick?.(val, objs);

        if (!rest.isMulti && val.length > 0) {
          focusNext?.();
        }
      }}
    />
  );
};
